package com.weareint.feigncopy;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

/**
 *
 *
 * <pre>
 * AnnotationMappingStrategy 利用@FeignMapping进行映射
 * @see   FeignMapping
 * </pre>
 *
 * @author johnny
 * @date 2021-07-23 5:47 下午
 */
@Slf4j
public class AnnotationMappingStrategy implements FeignMappingStrategy {

    @Override
    public void mapping(List<?> s, List<?> d) {
        if (log.isDebugEnabled()) {
            log.debug("AnnotationMappingStrategy mapping  d = {}", d);
        }
        Class<?> dClass = null;
        if (CollectionUtils.isNotEmpty(s) && CollectionUtils.isNotEmpty(d)) {
            Object o = d.get(0);
            dClass = o.getClass();
        }
        if (CollectionUtils.isEmpty(d)) {
            return;
        }
        Class<?> finalDestClass = dClass;

        s.forEach(
                item ->
                        Arrays.stream(item.getClass().getDeclaredFields())
                                .forEach(
                                        field -> {
                                            field.setAccessible(true);
                                            FeignMapping annotation =
                                                    field.getAnnotation(FeignMapping.class);
                                            if (annotation != null) {
                                                Class<?> aClass = annotation.beRelationClass();
                                                if (aClass.equals(finalDestClass)) {
                                                    // 关联的 key
                                                    String relationIdKey = annotation.relationId();
                                                    String beRelationIdKey =
                                                            annotation.beRelationId();
                                                    // 该属性 对应去解析的属性
                                                    try {
                                                        Field relationField =
                                                                item.getClass()
                                                                        .getDeclaredField(
                                                                                relationIdKey);
                                                        relationField.setAccessible(true);

                                                        Class<?> relationIdType =
                                                                annotation.relationIdType();
                                                        Object relationId =
                                                                relationIdType.cast(
                                                                        relationField.get(item));
                                                        d.forEach(
                                                                dItem -> {
                                                                    try {
                                                                        Field beRelationField;
                                                                        boolean flag =
                                                                                annotation
                                                                                        .beRelationIdIsSuper();
                                                                        if (flag) {
                                                                            beRelationField =
                                                                                    dItem.getClass()
                                                                                            .getSuperclass()
                                                                                            .getDeclaredField(
                                                                                                    beRelationIdKey);
                                                                        } else {
                                                                            beRelationField =
                                                                                    dItem.getClass()
                                                                                            .getDeclaredField(
                                                                                                    beRelationIdKey);
                                                                        }
                                                                        beRelationField
                                                                                .setAccessible(
                                                                                        true);

                                                                        Class<?> beRelationIdType =
                                                                                annotation
                                                                                        .beRelationIdType();
                                                                        Object beRelationId =
                                                                                beRelationIdType
                                                                                        .cast(
                                                                                                beRelationField
                                                                                                        .get(
                                                                                                                dItem));
                                                                        if (beRelationId != null) {
                                                                            // 2个id匹配
                                                                            if (beRelationId.equals(
                                                                                    relationId)) {
                                                                                // 找到  key 对应的数据
                                                                                String copyField =
                                                                                        annotation
                                                                                                .copyField();
                                                                                if (StringUtils
                                                                                        .isEmpty(
                                                                                                copyField)) {
                                                                                    copyField =
                                                                                            field
                                                                                                    .getName();
                                                                                }
                                                                                Field valueField;
                                                                                if (flag) {
                                                                                    valueField =
                                                                                            dItem.getClass()
                                                                                                    .getSuperclass()
                                                                                                    .getDeclaredField(
                                                                                                            copyField);
                                                                                } else {
                                                                                    valueField =
                                                                                            dItem.getClass()
                                                                                                    .getDeclaredField(
                                                                                                            copyField);
                                                                                }
                                                                                valueField
                                                                                        .setAccessible(
                                                                                                true);
                                                                                Object value =
                                                                                        valueField
                                                                                                .get(
                                                                                                        dItem);
                                                                                field.set(
                                                                                        item,
                                                                                        value);
                                                                            }
                                                                        }

                                                                    } catch (Exception e) {
                                                                        e.printStackTrace();
                                                                    }
                                                                });
                                                    } catch (Exception e) {
                                                        e.printStackTrace();
                                                    }
                                                }
                                            }
                                        }));
    }
}
